import { Mesh, MeshBasicMaterial, PlaneGeometry } from "three";
import { create } from "heatmap.js";
import DomUtils from "../../utils/DomUtils";
import { App } from "../../application";
import Config from "../../application/constants/Config";
import Helper from "../../utils/Helper";
import { Point } from "../../Docs";
import Const from "../../application/constants/Const";

class HeatMap extends Mesh {
    private heatmapDom;
    private app;
    private options;
    private heatMapInstance;
    public points: Array<{x: number, y: number, value: number}> = new Array<{x: number, y: number, value: number}>();
    private rangeWidth: number;
    private rangeHeight: number;
    private centerPoint: Point;
    private maxValue: number = -Infinity;
    private minValue: number = Infinity;

    constructor(options) {
        super();
        this.options = Object.assign(Config.HeatMapConfig, options);

        this.app = App.getInstance();

        this.heatmapDom = DomUtils.create("div", "", this.app.three.domElement);

        let config: any = {
            container: this.heatmapDom,
            radius: this.options.radius,
            scaleRadius: this.options.scaleRadius,
            maxOpacity: this.options.maxOpacity,
            minOpacity: this.options.minOpacity,
            blur: this.options.blur
        };
        this.heatMapInstance = create(config);
    }

    public setData(dataset: any) {
       this.handleData(dataset);
       this.createHeatmap();
    }

    private handleData(dataset: any) {
        if(!dataset) return;
        
        let maxX = Number.MIN_VALUE;
        let minX = Number.MAX_VALUE;
        let maxY = Number.MIN_VALUE;
        let minY = Number.MAX_VALUE;
        let self = this;
        dataset.forEach(element => {
            let point = new Point(element.lng, element.lat, 0, Const.EPSGType.EPSG84).toEPSGWeb();
            self.points.push({x: point.x, y: point.y, value: element.count});

            if(element.count > self.maxValue){
                self.maxValue = element.count;
            }
            if(element.count < self.minValue){
                self.minValue = element.count;
            }

            maxX = Math.max(maxX, point.x);
            minX = Math.min(minX, point.x);
            maxY = Math.max(maxY, point.y);
            minY = Math.min(minY, point.y);
        });
        
        this.rangeWidth = Math.max(Math.abs(maxX - minX), window.innerWidth);
        this.rangeWidth += this.rangeWidth / 10 + (this.options.radius || 1) * 5; //把边界扩大，是为了避免刚好在边界的点只绘制一部分
        this.rangeHeight = Math.max(Math.abs(maxY - minY), window.innerHeight);
        this.rangeHeight += this.rangeHeight / 10 + (this.options.radius || 1) * 5; //把边界扩大，是为了避免刚好在边界的点只绘制一部分
        this.centerPoint = new Point((maxX + minX)/2, (maxY + minY)/2, 0, Const.EPSGType.EPSGWeb);
    }

    private createHeatmap() {
        let canvasRatio = (this.rangeWidth / window.innerWidth);
        let canvasWidth = this.rangeWidth / canvasRatio;
        let canvasHeight = this.rangeHeight / canvasRatio;

        let widthScale = canvasWidth / this.rangeWidth;
        let heightScale = canvasHeight / this.rangeHeight;

        let self = this;
        this.points.forEach(element => {
            element.x = Math.floor( (self.rangeWidth / 2 + (element.x - self.centerPoint.x)) * widthScale );
            element.y = Math.floor( (self.rangeHeight / 2 - (element.y - self.centerPoint.y)) * heightScale );
        });

        let heatmapSize = {
            width: canvasWidth,
            height: canvasHeight
        };
        this.heatMapInstance.configure(heatmapSize);

        let dataCfg = {
            max: this.maxValue,
            min: this.minValue,
            data: this.points,
        }
        this.heatMapInstance.setData(dataCfg);

        this.geometry = new PlaneGeometry(this.rangeWidth, this.rangeHeight, 10, 10);
        (<MeshBasicMaterial>this.material).transparent = true;
        (<MeshBasicMaterial>this.material).map = Helper.createTexture(this.heatMapInstance.getDataURL(), null, true);
        (<MeshBasicMaterial>this.material).depthTest = false;

        this.position.copy(this.centerPoint)
    }
}
export { HeatMap }